/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sokoban.controller;

import java.awt.event.KeyEvent;
import sokoban.view.*;
import sokoban.model.Highscore;

import java.util.*;
import javax.swing.JPanel;

/**
 *
 * @author Otto
 */
public class Besturing {
    private MainWindow mainView;
    private Speelveld speelveldController;
    private HighscoreWindow Highscore;
    private SubmitHighscore SubmitHighscore;

    private HashMap highscores;
    private Integer level;
    private Boolean kindModus;
    private JPanel speelveld;
    private boolean spelBezig = false;
    private boolean spelerTrekt = false;

    /**
    * De constructor voor de Besturing klasse.
    * @param view De MainWindow klasse die zichzelf meegeeft zodat teruggecommuniceerd kan worden.
    **/
    public Besturing(MainWindow view){
        // De GUI geeft zichzelf mee bij het aanmaken van een besturing controller
        mainView = view;

        highscores = new HashMap<Integer, Highscore>();
    }

    /**
    * Start een nieuw spel op. Hierbij wordt een nieuw speelveld aangemaakt.
    * @param level Het level dat ingeladen gaat worden
    * @param kindModus Geeft aan of er gebruik wordt gemaakt van de kindmodus
    **/
    public void startSpel(Integer level, boolean kindModus){
        this.level = level;
        this.kindModus = kindModus;

        // Spel is bezig
        spelBezig = true;

        // Maak een speelveld controller aan
        speelveldController = new Speelveld(this, level, kindModus);

        // Haal de HashMap met elementen op
        speelveld = speelveldController.maakSpeelveld();

        // Toon het speelveld
        mainView.displaySpeelveld(speelveld, 0);

        // Voeg een keylistener aan het speelveld toe
        speelveld.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if(valideerToets(evt)){
                    switch (evt.getKeyCode()){
                        case KeyEvent.VK_SHIFT:
                            spelerTrekt = true;
                            break;
                        case KeyEvent.VK_UP:
                            doeZet("boven");
                            break;
                        case KeyEvent.VK_DOWN:
                            doeZet("onder");
                            break;
                        case KeyEvent.VK_RIGHT:
                            doeZet("rechts");
                            break;
                        case KeyEvent.VK_LEFT:
                            doeZet("links");
                            break;
                    }
                }
            }

            public void keyReleased(java.awt.event.KeyEvent evt) {
                if(valideerToets(evt)){
                    switch (evt.getKeyCode()){
                        case KeyEvent.VK_SHIFT:
                            spelerTrekt = false;
                            break;
                    }
                }
            }
        });
    }

    /**
    * Valideert of de toets, waar op gedrukt is, wel een pijltjestoets is
    * @param e Het KeyEvent dat getriggerd is door de toetsaanslag
    * @return Een boolean dat aangeeft of een pijltjestoets is aangeslagen of niet
    **/
    private Boolean valideerToets(KeyEvent e){
        Boolean valideToets = false;
        switch (e.getKeyCode()){
            case KeyEvent.VK_SHIFT:
                valideToets = true;
                break;
            case KeyEvent.VK_UP:
                valideToets = true;
                break;
            case KeyEvent.VK_DOWN:
                valideToets = true;
                break;
            case KeyEvent.VK_RIGHT:
                valideToets = true;
                break;
            case KeyEvent.VK_LEFT:
                valideToets = true;
                break;
        }
        return valideToets;
    }

    /**
    * Verschuift een speler een plaats in de richting die als parameter meegegeven wordt
    * @param richting De plaats waarin de speler verplaatst dient te worden
    **/
    public void doeZet(String richting){
        if (spelBezig){
            if (speelveldController.controleerZet(richting)){
                speelveldController.verplaatsElementen(richting, spelerTrekt);

                this.controleerSpel();

                speelveld = speelveldController.herinitialiseerSpeelveld(speelveld);

                // Haal de HashMap met elementen op en toon het speelveld
                mainView.displaySpeelveld(speelveld, speelveldController.getStappen());
            }
        }
    }

    /**
    * Voegt een highscore toe indien het level nog niet bestaat of indien de score
    * van het gespeelde level hoger is dan het bestaande level in de highscores HashMap
    * @param score De score van het zojuist uitgespeelde level
     *@param naam De naam van de speler die het level uitgespeeld heeft
    **/
    public void voegHighscoreToe(Integer score, String naam){
        Integer key = -1;
        int highScoreLevel = level;

        if(kindModus){
            highScoreLevel = highScoreLevel + 12;
        }

        Map<Integer, Object> map = this.highscores;
        for (Integer tmpKey : map.keySet()){
            if (highScoreLevel == tmpKey){
                key = tmpKey;
                break;
            }
        }

        if (key == -1)
        {
            // Nieuw level
            Highscore highscore = new Highscore(highScoreLevel, score, naam);
            highscores.put(highScoreLevel, highscore);
        }
        else
        {
            // Bestaand level
            Highscore highscore = (Highscore)highscores.get(key);
            if (highscore.getScore() > score)
            {
                highscore.setNaam(naam);
                highscore.setScore(score);
            }
        }
    }

    /**
    * Leegt de highscores HashMap
    **/
    public void leegHighscores(){
        highscores.clear();
    }

    /**
    * Geeft de highscores HashMap terug
    * @return HashMap met de highscores van de gespeelde levels
    **/
    public HashMap<Integer, Highscore> getHighscores(){
        return highscores;
    }

    /**
    * Controleert of de dozen uit het level op alle doelen staan.
    * Als dit zo is, wordt een scherm geopend waar de gebruiker zijn naam op kan geven
    * voor het opslaan van zijn highscore.
    **/
    private void controleerSpel(){
        if (speelveldController.controleerDoelen()){
            spelBezig = false;
            this.openSubmitHighscore(String.valueOf(speelveldController.getStappen()));
        }
    }

    /*
    * Sluit het programma Sokoban af
    */
    public void sluitSokoban(){
        if(Highscore != null){Highscore.setVisible(false);}
        System.exit(0);
    }

    /*
    * Opent een highscore venster, dat alle highscores weergeeft
    */
    public void openHighscoreWindow(){
        Highscore = new HighscoreWindow(this.getHighscores());
        Highscore.setVisible(true);
    }

    /*
    * Opent een submit highscore venster waarbij de gebruiker zijn naam op kan geven
    * @param highscore De score van het zojuist uitgespeelde level
    */
    public void openSubmitHighscore(String highscore){
        SubmitHighscore = new SubmitHighscore(this, highscore);
        SubmitHighscore.setVisible(true);
        SubmitHighscore.toFront();
        SubmitHighscore.setAlwaysOnTop(true);
    }

    /**
    * Brengt de speler in een level terug naar zijn begin positie
    */
    public void brengSpelerThuis(){
        // Vervang de coordinaten van de speler
        if (speelveldController.brengSpelerThuis()){

            // Controleer het spel
            this.controleerSpel();

            // Herinitialiseer het speelveld
            speelveld = speelveldController.herinitialiseerSpeelveld(speelveld);

            // Haal de HashMap met elementen op en toon het speelveld
            mainView.displaySpeelveld(speelveld, speelveldController.getStappen());
        }
    }

    /**
    * Geeft het level terug
    * @return Het level waar op dit moment mee gespeeld wordt
    **/
    public Integer getLevel() {
        return level;
    }

    /**
    * Zet een level
    **/
    public void SetLevel(Integer level) {
        this.level = level;
    }
}
